


struct VS_IN
{
	float3	position	:	POSITION;
	float3	normal		:	NORMAL;
	float3	tangent		:	TANGENT;
	float2	tex			:	TEX_COORDS;
};
//____________
struct ControlPoint
{
	float3	position	:	POSITION;
};
//____________
struct quadTessFactors
{
	float	edge[4]		:	SV_TessFactor;
	float	inside[2]	:	SV_InsideTessFactor;
};
//____________
struct DS_OUT
{
	float4	position	:	SV_POSITION;
	float3  wNormal		:	NORMAL;
};

//________ V A R I A B L E S____________________________________________________________________________

float4x4	m_World;
float4x4	m_WorldViewProj;
float		fInsideTess;
float		fEdgeTess;
float		fDelta;
float3		vPosition;
float3		vTargetPos;


static const float z5 = -1;

float3 G[5][5] = {	
					float3(-1,2,z5),  float3( -1,2,z5),  float3(0,4,z5),  float3(1,2,z5),  float3(1,2,z5),
					float3(-6,1,z5),  float3(-1,1,0),  float3(0,1,0),  float3(1,1,0),  float3(6,1,z5),
					float3(-7,0,z5),  float3(-1,0,0),  float3(0,0,0),  float3(1,0,0),  float3(7,0,z5),
					float3(-2,-0.0,z5), float3(0,-0.0,0), float3(0,-0.0,0), float3(0,-0.0,0), float3(2,-0.0,z5),
					float3(-0.2,-2,z5), float3(-0.1,-3,z5), float3(0,-3,z5), float3(0.1,-3,z5), float3(0.2,-2,z5)
				};




//________ F U N C T I O N S________________________________________________________________________________


float H0(float x)
{
	float res = 0.5f;

	if (x < 0.0f)
		res = 0.0f;
	else if (x > 0.0f)
		res = 1.0f;
		
	return res;
}

float H3d(float x)
{
	float res = 0.0f;

	res +=  (x+3)*(x+3)   * H0(x+3);
	res += -3*(x+1)*(x+1) * H0(x+1);
	res +=  3*(x-1)*(x-1) * H0(x-1);
	res += -(x-3)*(x-3)   * H0(x-3);

	res *= 0.0625;
	return res;
}


float H3(float x)
{
	float res = 0.0f;
	bool posVal = false;

	if (x > 0.0f) {
		posVal = true;
		x = -x;
	}

	if (x < -3.0f)
		res = 0.0f;

	else if (x >= -3.0f && x < -1.0f)
		res = 0.02f * (3.0f+x)*(3.0f+x)*(3.0f+x);

	else if (x >= -1.0f && x <= 0.0f)
		res = 0.042f * (12.0f + 9.0f*x - x*x*x);

	if (posVal)
		res = 1.0f - res;

	return res;
}

float H3_delta(float x, float delta)
{
	return H3((3.0f * x) / delta);
}

float B(float x, float delta, float a, float b)
{
	return (H3_delta(x-a, delta) - H3_delta(x-b, delta));
}



float H3d_delta(float x, float delta)
{
	return H3d((3.0f * x) / delta) * (3.0f / delta);
}

float Bd(float x, float delta, float a, float b)
{
	return (H3d_delta(x-a, delta) - H3d_delta(x-b, delta));
}





//________ S H A D E R S________________________________________________________________________________


ControlPoint VS_BezSurface(VS_IN vin)
{
	ControlPoint vout;

	vout.position = vin.position;
	return vout;
}
//_______________
											// ctrl points per patch
quadTessFactors ConstantHS_BezSurface(InputPatch<ControlPoint, 1> patch, uint patchID : SV_PrimitiveID) // Constant Hull Shader
{
	quadTessFactors tout;

	tout.edge[0] = fEdgeTess;
	tout.edge[1] = fEdgeTess;
	tout.edge[2] = fEdgeTess;
	tout.edge[3] = fEdgeTess;
	
	tout.inside[0] = fInsideTess;
	tout.inside[1] = fInsideTess;

	return tout;
}
//_______________

[domain("quad")]
[partitioning("integer")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(9)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_Integer(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = G[ctrlPoint_index/3][ctrlPoint_index % 3];
	return p;
}
//_______________
[domain("quad")]
[partitioning("fractional_even")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(9)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracEven(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = G[ctrlPoint_index/3][ctrlPoint_index % 3];
	return p;
}
//_______________
[domain("quad")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[outputcontrolpoints(9)]
[patchconstantfunc("ConstantHS_BezSurface")]
[maxtessfactor(64.0f)]
ControlPoint ControlPointHS_BezSurface_FracOdd(InputPatch<ControlPoint, 1> patch,	   uint ctrlPoint_index : SV_OutputControlPointID, 
																		       uint patchId	     	: SV_PrimitiveID)
{

	// Output 4x4 control points
	ControlPoint p;
	p.position = G[ctrlPoint_index/3][ctrlPoint_index % 3];
	return p;
}
//_______________

float fScale;


float3 evaluatePSPSurface(float2 uv, out float3 normal)
{
	float3 res = float3(0,0,0);
	int i, n = 2;
	float dim = 5.0f;
	float s = 1.0f / dim;
	
	float a[6] = { 0.0f, s, s*2, s*3, s*4, s*5 };
	float b[6] = { 0.0f, s, s*2, s*3, s*4, s*5 };

	for (i=0; i<dim; i++) {
		for (int j=0; j<dim; j++) {
			res +=	(G[i][j].xzy) * 
					B(uv.x-0.5, fDelta, a[i], a[i+1]) * 
					B(uv.y-0.5, fDelta, b[j], b[j+1]);
		}
	}

	float3 norm = (float3)0.0f;

	for (i=0; i<dim; i++) {
		for (int j=0; j<dim; j++) {
			norm +=	(G[i][j].xzy) * 
					Bd(uv.x-0.5, fDelta, a[i], a[i+1]) * 
					Bd(uv.y-0.5, fDelta, b[j], b[j+1]);
		}
	}
	normal = norm;

	return res;
}

[domain("quad")]
DS_OUT	DS_BezSurface(quadTessFactors tessFactors,	float2 coords : SV_DomainLocation, 
												const OutputPatch<ControlPoint, 9> patch)
{
	DS_OUT dout;

	
	float3 quad[4];
	quad[0] = float3(-1,1,0);
	quad[1] = float3(1,1,0);
	quad[2] = float3(-1,-1,0);
	quad[3] = float3(1,-1,0);

	float3 pos = (1-coords.y) * ((1-coords.x) * quad[0] + coords.x * quad[1]) +
		         coords.y  * ((1-coords.x) * quad[2] + coords.x * quad[3]);

	float3 n;
	float3 psp = evaluatePSPSurface(coords*2.0f, n);
		
	float3 Z = normalize(vTargetPos - vPosition);
	float3 Y = float3(0,1,0);
	float3 X = normalize(cross(Z, Y));

	psp = mul(psp, float3x3(X,Y,Z));
	n = mul(n, float3x3(X,Y,Z));

	dout.wNormal = normalize(n); 
	
	float3 fPos = vPosition;
	fPos.y = 15.0f;
	psp += fPos;

	dout.position = mul(float4(psp,1.0f), m_WorldViewProj);

	return dout;
}

//_______________


float4 PS_BezSurface(DS_OUT pin) : SV_TARGET
{

	float3 L = float3(0, 1 ,0);
	float shade = dot(pin.wNormal, L);
	return float4(shade, shade, shade, 1.0f);
}


//###################################################################################


DS_OUT VS_NoTess(VS_IN vin)
{
	DS_OUT dout = (DS_OUT)0;
	dout.position = mul(float4(vin.position, 1.0f), m_WorldViewProj);

	return dout;

}

float4 PS_NoTess(DS_OUT pin) : SV_TARGET
{
	return float4(1,0,0,1);
}

//###################################################################################


RasterizerState rs_Wireframe
{
	CullMode = Back;
	FillMode = Wireframe;
};

RasterizerState	rs_solidNone
{
	CullMode = Back;
	FillMode = Solid;
};


technique11 TessPSPSurface_Integer
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_Integer() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}

technique11 TessPSPSurface_FracEven
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracEven() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}


technique11 TessPSPSurface_FracOdd
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_BezSurface()   ) );
		SetHullShader ( CompileShader ( hs_5_0, ControlPointHS_BezSurface_FracOdd() ) );
		SetDomainShader( CompileShader ( ds_5_0, DS_BezSurface() ) );
		SetGeometryShader( NULL );
		SetPixelShader( CompileShader( ps_5_0,   PS_BezSurface()   ) );
	}
}



technique11 NoTess
{
	pass P0
	{
		SetVertexShader( CompileShader( vs_5_0,  VS_NoTess()   ) );
		SetPixelShader( CompileShader( ps_5_0,   PS_NoTess()   ) );
		
		SetRasterizerState(rs_Wireframe);
	}


}